package com.cg.leetcode;

import org.junit.Test;

/**
 * 106.从中序与后序遍历序列构造二叉树
 *
 * @program: LeetCode->LeetCode_106
 * @author: cg
 * @create: 2022-04-25 09:50
 **/
public class LeetCode_106 {

    @Test
    public void test106() {
        System.out.println(buildTree(new int[]{9, 3, 15, 20, 7}, new int[]{9, 15, 7, 20, 3}));
    }

    /**
     * 给定两个整数数组 inorder 和 postorder ，其中 inorder 是二叉树的中序遍历， postorder 是同一棵树的后序遍历，请你构造并返回这颗 二叉树 。
     * <p>
     * 示例 1:
     * 输入：inorder = [9,3,15,20,7], postorder = [9,15,7,20,3]
     * 输出：[3,9,20,null,null,15,7]
     * <p>
     * 示例 2:
     * 输入：inorder = [-1], postorder = [-1]
     * 输出：[-1]
     * <p>
     * 提示:
     * 1 <= inorder.length <= 3000
     * postorder.length == inorder.length
     * -3000 <= inorder[i], postorder[i] <= 3000
     * inorder 和 postorder 都由 不同 的值组成
     * postorder 中每一个值都在 inorder 中
     * inorder 保证是树的中序遍历
     * postorder 保证是树的后序遍历
     *
     * @param inorder
     * @param postorder
     * @return
     */
    public TreeNode buildTree(int[] inorder, int[] postorder) {
        return dfs(inorder, 0, inorder.length, postorder, 0, postorder.length);
    }

    private TreeNode dfs(int[] inorder, int inLeft, int inRight, int[] postoder, int postLeft, int postRight) {
        /*
         * 第一步：如果数组大小为零的话，说明是空节点了。
         * 第二步：如果不为空，那么取后序数组最后一个元素作为节点元素。
         * 第三步：找到后序数组最后一个元素在中序数组的位置，作为切割点
         * 第四步：切割中序数组，切成中序左数组和中序右数组 （顺序别搞反了，一定是先切中序数组）
         * 第五步：切割后序数组，切成后序左数组和后序右数组
         * 第六步：递归处理左区间和右区间
         */
        if (inLeft >= inRight || postLeft >= postRight) {
            return null;
        }
        // 后序遍历中的最后一个节点就是根节点
        int rootVal = postoder[postRight -1 ];
        TreeNode root = new TreeNode(rootVal);
        // 在中序遍历中定位根节点
        int rootIndex = 0;
        for (int i = inLeft; i < inRight; i++) {
            if (inorder[i] == rootVal) {
                rootIndex = i;
                break;
            }
        }
        // 得到左子树中的节点数目
        int size = rootIndex - inLeft;
        // 递归地构造左子树，并连接到根节点
        // 后序遍历中「从 左边界 开始到 右边界+size」的元素就对应了中序遍历中「左边界 开始到 根节点定位」的元素
        root.left = dfs(inorder, inLeft, rootIndex, postoder, postLeft, postLeft + size);
        // 递归地构造右子树，并连接到根节点
        // 后序遍历中「从 左边界 开始的 size」个元素就对应了中序遍历中「从 根节点定位+1 到 右边界」的元素
        root.right = dfs(inorder, rootIndex + 1, inRight, postoder, postLeft + size, postRight - 1);
        return root;
    }

}
